package com.marver.common.utils;

import com.marver.common.exception.HmException;
import com.marver.common.result.Message;
import com.marver.common.result.Result;
import com.marver.common.result.ResultStatus;
import org.apache.commons.lang3.StringUtils;

import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import java.text.DateFormat;
import java.text.ParseException;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class ValidDataUtil {
	private static Validator validator;

	static {
		validator = Validation.buildDefaultValidatorFactory().getValidator();
	}
	
	//    位权值数组
	private static byte[] Wi=new byte[17];
	//	    身份证前部分字符数
	private static final byte fPart = 6;
	//	    身份证算法求模关键值
	private static final byte fMod = 11;
	//	    旧身份证长度
	private static final byte oldIDLen = 15;
	//	    新身份证长度
	private static final byte newIDLen = 18;
	//	    新身份证年份标志
	private static final String yearFlag = "19";
	//	    校验码串 
	private static final String CheckCode="10X98765432"; 
	//	    最小的行政区划码
//	private static final int minCode = 150000;
	//	    最大的行政区划码
//	private static final int maxCode = 700000;
	
//	private static String birthday = "";
	
	/**
	* 验证邮箱
	* 
	* @param str 待验证的字符串
	* @return 如果是符合的字符串,返回 <b>true </b>,否则为 <b>false </b>
	*/
	public static boolean isEmail(String str) {
	String regex = "^([\\w-\\.]+)@((\\[[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.)|(([\\w-]+\\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\\]?)$";
	return match(regex, str);
	}

	/**
	* 验证IP地址
	* 
	* @param str 待验证的字符串
	* @return 如果是符合格式的字符串,返回 <b>true </b>,否则为 <b>false </b>
	*/
	public static boolean isIP(String str) {
	String num = "(25[0-5]|2[0-4]\\d|[0-1]\\d{2}|[1-9]?\\d)";
	String regex = "^" + num + "\\." + num + "\\." + num + "\\." + num + "$";
	return match(regex, str);
	}

	/**
	* 验证网址Url
	* 
	* @param str 待验证的字符串
	* @return 如果是符合格式的字符串,返回 <b>true </b>,否则为 <b>false </b>
	*/
	public static boolean IsUrl(String str) {
	String regex = "http(s)?://([\\w-]+\\.)+[\\w-]+(/[\\w- ./?%&=]*)?";
	return match(regex, str);
	}

	/**
	* 验证电话号码
	* 
	* @param str 待验证的字符串
	* @return 如果是符合格式的字符串,返回 <b>true </b>,否则为 <b>false </b>
	*/
	public static boolean IsTelephone(String str) {
	String regex = "^(\\d{3,4}-)?\\d{6,8}$";
	return match(regex, str);
	}

	/**
	* 验证输入密码条件(字符与数据同时出现)
	*
	* @param str 待验证的字符串
	* @return 如果是符合格式的字符串,返回 <b>true </b>,否则为 <b>false </b>
	*/
	public static boolean IsPassword(String str) {
	String regex = "[A-Za-z]+[0-9]";
	return match(regex, str);
	}

	/**
	* 验证输入密码长度 (6-18位)
	* 
	* @param str 待验证的字符串
	* @return 如果是符合格式的字符串,返回 <b>true </b>,否则为 <b>false </b>
	*/
	public static boolean IsPasswLength(String str) {
	String regex = "^\\d{6,18}$";
	return match(regex, str);
	}

	/**
	* 验证输入邮政编号
	* 
	* @param str 待验证的字符串
	* @return 如果是符合格式的字符串,返回 <b>true </b>,否则为 <b>false </b>
	*/
	public static boolean IsPostalcode(String str) {
	String regex = "^\\d{6}$";
	return match(regex, str);
	}

	/**
	* 验证输入手机号码
	* 
	* @param str 待验证的字符串
	* @return 如果是符合格式的字符串,返回 <b>true </b>,否则为 <b>false </b>
	*/
	public static boolean IsHandset(String str) {
	String regex = "^[1]+[3,5]+\\d{9}$";
	return match(regex, str);
	}

	/**
	* 验证输入身份证号
	* 
	* @param str 待验证的字符串
	* @return 如果是符合格式的字符串,返回 <b>true </b>,否则为 <b>false </b>
	*/
	public static boolean IsIDcard(String str) {
	String regex = "(^\\d{18}$)|(^\\d{15}$)";
	return match(regex, str);
	}

	/**
	* 验证输入两位小数
	* 
	* @param str 待验证的字符串
	* @return 如果是符合格式的字符串,返回 <b>true </b>,否则为 <b>false </b>
	*/
	public static boolean IsDecimal(String str) {
	String regex = "^[0-9]+(.[0-9]{2})?$";
	return match(regex, str);
	}
	
	/**
	* 验证输入数字长度 (1-18位)
	* 
	* @param str 待验证的字符串
	* @return 如果是符合格式的字符串,返回 <b>true </b>,否则为 <b>false </b>
	*/
	public static boolean IsAmountLength(String str) {
	String regex = "^\\d{1,12}$";
	return match(regex, str);
	}

	/**
	* 验证输入一年的12个月
	* 
	* @param str 待验证的字符串
	* @return 如果是符合格式的字符串,返回 <b>true </b>,否则为 <b>false </b>
	*/
	public static boolean IsMonth(String str) {
	String regex = "^(0?[[1-9]|1[0-2])$";
	return match(regex, str);
	}

	/**
	* 验证输入一个月的31天
	* 
	* @param str 待验证的字符串
	* @return 如果是符合格式的字符串,返回 <b>true </b>,否则为 <b>false </b>
	*/
	public static boolean IsDay(String str) {
	String regex = "^((0?[1-9])|((1|2)[0-9])|30|31)$";
	return match(regex, str);
	}

	/**
	* 验证日期时间
	* 
	* @param str 待验证的字符串
	* @return 如果是符合网址格式的字符串,返回 <b>true </b>,否则为 <b>false </b>
	*/
	public static boolean isDate(String str) {
	// 严格验证时间格式的(匹配[2002-01-31], [1997-04-30],
	// [2004-01-01])不匹配([2002-01-32], [2003-02-29], [04-01-01])
	// String regex =
	// "^((((19|20)(([02468][048])|([13579][26]))-02-29))|((20[0-9][0-9])|(19[0-9][0-9]))-((((0[1-9])|(1[0-2]))-((0[1-9])|(1\\d)|(2[0-8])))|((((0[13578])|(1[02]))-31)|(((01,3-9])|(1[0-2]))-(29|30)))))$";
	// 没加时间验证的YYYY-MM-DD
	// String regex =
	// "^((((1[6-9]|[2-9]\\d)\\d{2})-(0?[13578]|1[02])-(0?[1-9]|[12]\\d|3[01]))|(((1[6-9]|[2-9]\\d)\\d{2})-(0?[13456789]|1[012])-(0?[1-9]|[12]\\d|30))|(((1[6-9]|[2-9]\\d)\\d{2})-0?2-(0?[1-9]|1\\d|2[0-8]))|(((1[6-9]|[2-9]\\d)(0[48]|[2468][048]|[13579][26])|((16|[2468][048]|[3579][26])00))-0?2-29-))$";
	// 加了时间验证的YYYY-MM-DD 00:00:00
	String regex = "^((((1[6-9]|[2-9]\\d)\\d{2})-(0?[13578]|1[02])-(0?[1-9]|[12]\\d|3[01]))|(((1[6-9]|[2-9]\\d)\\d{2})-(0?[13456789]|1[012])-(0?[1-9]|[12]\\d|30))|(((1[6-9]|[2-9]\\d)\\d{2})-0?2-(0?[1-9]|1\\d|2[0-8]))|(((1[6-9]|[2-9]\\d)(0[48]|[2468][048]|[13579][26])|((16|[2468][048]|[3579][26])00))-0?2-29-)) (20|21|22|23|[0-1]?\\d):[0-5]?\\d:[0-5]?\\d$";
	return match(regex, str);
	}

	/**
	* 验证数字输入
	* 
	* @param str 待验证的字符串
	* @return 如果是符合格式的字符串,返回 <b>true </b>,否则为 <b>false </b>
	*/
	public static boolean IsNumber(String str) {
	String regex = "^[0-9]*$";
	return match(regex, str);
	}

	/**
	* 验证非零的正整数
	* 
	* @param str 待验证的字符串
	* @return 如果是符合格式的字符串,返回 <b>true </b>,否则为 <b>false </b>
	*/
	public static boolean IsIntNumber(String str) {
	if(str==null||str.isEmpty()){
		return false;
	}
	String regex = "^\\+?[1-9][0-9]*$";
	return match(regex, str);
	}

	/**
	* 验证大写字母
	* 
	* @param str 待验证的字符串
	* @return 如果是符合格式的字符串,返回 <b>true </b>,否则为 <b>false </b>
	*/
	public static boolean IsUpChar(String str) {
	String regex = "^[A-Z]+$";
	return match(regex, str);
	}

	/**
	* 验证小写字母
	* 
	* @param str 待验证的字符串
	* @return 如果是符合格式的字符串,返回 <b>true </b>,否则为 <b>false </b>
	*/
	public static boolean IsLowChar(String str) {
	String regex = "^[a-z]+$";
	return match(regex, str);
	}

	/**
	* 验证验证输入字母
	* 
	* @param str 待验证的字符串
	* @return 如果是符合格式的字符串,返回 <b>true </b>,否则为 <b>false </b>
	*/
	public static boolean IsLetter(String str) {
	String regex = "^[A-Za-z]+$";
	return match(regex, str);
	}

	/**
	* 验证验证输入汉字
	* 
	* @param str 待验证的字符串
	* @return 如果是符合格式的字符串,返回 <b>true </b>,否则为 <b>false </b>
	*/
	public static boolean IsChinese(String str) {
	String regex = "^[\u4e00-\u9fa5],{0,}$";
	return match(regex, str);
	}

	/**
	* 验证验证输入字符串
	* 
	* @param str 待验证的字符串
	* @return 如果是符合格式的字符串,返回 <b>true </b>,否则为 <b>false </b>
	*/
	public static boolean IsLength(String str) {
	String regex = "^.{8,}$";
	return match(regex, str);
	}
	
	/**
	 * 验证手机格式
	 * @param mobilenumber
	 * @return
	 */
	public static boolean isMobile(String mobilenumber) {   
        Pattern p = null;  
        Matcher m = null;  
        boolean isSuccess = false;   
        // /^(((13[0-9]{1})|(15[0-9]{1})|(18[0-9]{1})|(14[0-9]{1})|(17[0-9]{1}))+\d{8})$/
        p = Pattern.compile("^(((13[0-9]{1})|(15[0-9]{1})|(18[0-9]{1})|(14[0-9]{1})|(17[0-9]{1}))+\\d{8})$"); // 验证手机号  
        m = p.matcher(mobilenumber);  
        isSuccess = m.matches();   
        return isSuccess;  
    }
	
	    
    //private String Area[][2] = 
    private static void setWiBuffer(){
        for(int i=0;i<Wi.length;i++){    
            int k = (int) Math.pow(2, (Wi.length-i));
            Wi[i] = (byte)(k % fMod);
        }
    }
	    
    //获取新身份证的最后一位:检验位
    private static String getCheckFlag(String idCard){
        int sum = 0;
        //进行加权求和 
        for(int i=0; i<17; i++){        
            sum += Integer.parseInt(idCard.substring(i,i+1)) * Wi[i];
        }
        //取模运算，得到模值
        byte iCode = (byte) (sum % fMod); 
        return CheckCode.substring(iCode,iCode+1);
    }
    
    //判断串长度的合法性
    private static boolean checkLength(final String idCard,boolean newIDFlag){
        boolean right = (idCard.length() == oldIDLen) || (idCard.length() == newIDLen);
        newIDFlag = false;
        if(right){
            newIDFlag = (idCard.length() == newIDLen);
        }
        return right;
    }
    
    //获取时间串
    private static String getIDDate(final String idCard,boolean newIDFlag){
        String dateStr = "";
        if(newIDFlag)
            dateStr = idCard.substring(fPart,fPart+8);
        else
            dateStr = yearFlag + idCard.substring(fPart,fPart+6);
        return dateStr;
    }
    
    //判断时间合法性
    private static boolean checkDate(final String dateSource){
        String dateStr = dateSource.substring(0,4)+"-"+dateSource.substring(4,6)+"-"+dateSource.substring(6,8);
//        birthday = dateStr;
        DateFormat df = DateFormat.getDateInstance();
        df.setLenient(false);
        try {
            Date date= df.parse(dateStr);
            return (date!=null);
        } catch (ParseException e) {
            // TODO Auto-generated catch block
            return false;
        }
    }
    
    //旧身份证转换成新身份证号码
    public static String getNewIDCard(final String oldIDCard){
        //初始化方法
        setWiBuffer();
        if(!checkIDCard(oldIDCard)){
            return oldIDCard;
        }
        String newIDCard = oldIDCard.substring(0, fPart);
        newIDCard += yearFlag;
        newIDCard += oldIDCard.substring(fPart, oldIDCard.length());
        String ch = getCheckFlag(newIDCard);
        newIDCard += ch;
        return newIDCard;
    }
    
    //新身份证转换成旧身份证号码
    public static String getOldIDCard(final String newIDCard){
        //初始化方法
        setWiBuffer();
        if(!checkIDCard(newIDCard)){
            return newIDCard;
        }
        String oldIDCard = newIDCard.substring(0,fPart)+
                    newIDCard.substring(fPart+yearFlag.length(),newIDCard.length()-1);
        return oldIDCard;
    }
    
    //判断身份证号码的合法性
    public static boolean checkIDCard(final String idCard){
        //初始化方法
        setWiBuffer();
        boolean isNew = true;
        //String message = "";
        if (!checkLength(idCard,isNew)){
            //message = "ID长度异常";
            return false;
        }
        String idDate = getIDDate(idCard, isNew);
        if(!checkDate(idDate)){
            //message = "ID时间异常";
            return false;
        }
        if(isNew){
            String checkFlag = getCheckFlag(idCard);
            String theFlag = idCard.substring(idCard.length()-1,idCard.length());
            if(!checkFlag.equals(theFlag)){
                //message = "新身份证校验位异常";
                return false;
            }
        }
        return true;
    }
	/**
	 * 验证有效的价格
	 * @author mcx
	 * @param price 传入的价格
	 * @return
	 */
	public static boolean validPrice(String price){
		//String veg = "\\d+(\\.\\d+)?" ;//验证amount价格：必须是正数/小数的价格
		if(StringUtils.isBlank(price)){
			return false ;
		}
		if(StringUtils.isNumericSpace(price)){
			if(Double.valueOf(price)<=0){
				return false ;
			}
		}
		String veg = "[0-9]+(.[0-9]{0,2})?" ;//验证amount价格：必须是正数/小数的价格
		return price.matches(veg) ;
	}
	/**
	 * 验证非0正整数
	 * @author mcx
	 * @param count
	 * @return
	 */
	public static boolean validCount(String count){
		if(StringUtils.isBlank(count)){
			return false ;
		}
		if(StringUtils.isNumeric(count)){
			if(Double.valueOf(count)<=0){
				return false ;
			}
		}
		String veg = "\\d+" ;//验证数量
		return count.matches(veg) ;
	}

	/**
	* @param regex
	* 正则表达式字符串
	* @param str
	* 要匹配的字符串
	* @return 如果str 符合 regex的正则表达式格式,返回true, 否则返回 false;
	*/
	public static boolean match(String regex, String str) {
		Pattern pattern = Pattern.compile(regex);
		Matcher matcher = pattern.matcher(str);
		return matcher.matches();
	}
	/**
	 * 包含特殊字符
	 * @author dengyu
	 * @param str
	 * @return
	 */
	public static boolean hasSpecial(String str) {   
        Pattern p = null;  
        Matcher m = null;  
        boolean isSuccess = false;
        p = Pattern.compile("^.*[/|\\\\|\\*|\\?|\"|<|>].*$");
        m = p.matcher(str);  
        isSuccess = m.matches();   
        return isSuccess;
    }

	/**
	 * 参数校验(非空:required|超长:maxlength=20|数字:number|正整数:int|3位以内小数:decimal|特殊字符:special|电子邮箱:email|手机号码:mobile|身份证号:idCard)
	 * @author marver
	 * @param key 参数key值
	 * @param rules 校验规则|隔开
	 * @param keyName 参数别名
	 * @param resultMsg 返回结果
	 * @return
	 */
	public static Result checkData(Object key, String rules, String keyName, Result resultMsg) {
		Message msg = new Message();
		if(key == null && rules.indexOf("required") != -1){
			msg.setTip("【"+keyName+"】为空");
			resultMsg.setCode(ResultStatus.INVALID_PARAM.getErrorCode());
			resultMsg.setMessage(msg);
			return resultMsg;
		}else if(key == null && rules.indexOf("required") == -1){
			return null;//参数为空且允许参数为空,不做后续校验
		}

		if(resultMsg == null) {
			resultMsg = new Result();
		}
		if(ResultStatus.INVALID_PARAM.getErrorCode() == resultMsg.getCode()){//有错误则不会继续校验
			return resultMsg;
		}
		String validMsg = null;
		if(rules.indexOf("|") == -1){
			validMsg = ValidDataUtil.check(key.toString(), rules, keyName);
			if(validMsg != null){
				msg.setTip(validMsg);
				resultMsg.setCode(ResultStatus.INVALID_PARAM.getErrorCode());
				resultMsg.setMessage(msg);
			}
		}else{
			String[] arr = rules.split("\\|");
			for (int i = 0; i < arr.length; i++) {
				validMsg = ValidDataUtil.check(key.toString(), arr[i], keyName);
				if(validMsg != null){
					msg.setTip(validMsg);
					resultMsg.setCode(ResultStatus.INVALID_PARAM.getErrorCode());
					resultMsg.setMessage(msg);
					break;
				}
			}
		}
		if(ResultStatus.INVALID_PARAM.getErrorCode() == resultMsg.getCode()){
			msg.setTip(validMsg);
			resultMsg.setCode(ResultStatus.INVALID_PARAM.getErrorCode());
			resultMsg.setMessage(msg);
		}
		return resultMsg;
	}
	
	/**
	 * 参数校验(非空:required|超长:maxlength=?|字母:letter|数字:number|正整数:int|3位以内小数:decimal|特殊字符:special|电子邮箱:email|手机号码:mobile|身份证号:idCard|日期:date)
	 * @author dengyu
	 * @param param 参数
	 * @param rules 校验规则|隔开
	 * @param msg 参数别名
	 * @param result 校验结果接收
	 * @return
	 */
	public static String checkParam(Object param,String rules,String msg,Map<String,Object> result) {
		if(result == null) {
			result = new HashMap<String,Object>();
		}
		String str = null;
		if(param != null){
			str = param.toString();
		}
		String validMsg = null;
    	if(rules.indexOf("|") == -1){
    		validMsg = ValidDataUtil.check(str, rules, msg);
    	}else{
    		String[] arr = rules.split("\\|");
    		for (int i = 0; i < arr.length; i++) {
    			validMsg = ValidDataUtil.check(str, arr[i], msg);
    			if(validMsg != null){
    				result.put("message", validMsg);
    				break;
    			}
			}
    	}
        return validMsg;
    }
	/**
	 * 参数校验
	 * @author dengyu
	 * @param param
	 * @param rule
	 * @param keyName
	 * @return
	 */
	public static String check(String param,String rule,String keyName) {
		try {
			if(param != null){
				param = param.trim();
			}
			keyName = "【" + keyName + "】";
			if("required".equals(rule)){//非空校验
				if(StringUtils.isBlank(param)){
					return keyName+"不能为空";
				}
	        }else if(param != null && rule.indexOf("maxlength") > -1 && rule.indexOf("=") > -1){//长度校验
				int lenth = Integer.valueOf(rule.split("=")[1]);
				String sParam = (String)param;
				if(sParam.length() > lenth){
					return keyName+"过长";
				}
	        }else if(param != null && rule.indexOf("minlength") > -1 && rule.indexOf("=") > -1){//长度校验
				int lenth = Integer.valueOf(rule.split("=")[1]);
				String sParam = (String)param;
				if(sParam.length() < lenth){
					return keyName+"位数太短";
				}
			}else if(param != null && "letter".equals(rule)){//纯字母校验
				String regex = "^[A-Za-z]+$";
				if(!match(regex, param)){
					return keyName+"格式错误，请输入字母";
				}
			}else if(param != null && "number".equals(rule)){//纯数字校验
				String regex = "^[0-9]*$";
				if(!match(regex, param)){
					return keyName+"格式错误，请输入数字";
				}
	        }else if(param != null && "int".equals(rule)){//非0正整数校验（10.00可通过）
				String regex = "^[0-9]+(.[0]{0,2})?$";
				if(param.indexOf(".") != -1 && param.indexOf("E") != -1){
					regex = "^[0-9]+(.[0-9]+[E]+[0-9]{0,10})?$";//科学计数法
				}else if(!match(regex, param) || Double.valueOf(param.toString()) <= 0){
					return keyName+"格式错误，请输入正整数";
				}
	        }else if(param != null && "mo100".equals(rule)){//非0正整数校验（100的整数倍）
	        	double iParam = Double.valueOf(param);
				if(iParam%100 != 0){
					return keyName+"格式错误，请输入100的整数倍";
				}
	        }else if(param != null && "mo1000".equals(rule)){//非0正整数校验（1000的整数倍）
				double iParam = Double.valueOf(param);
				if(iParam%1000 != 0){
					return keyName+"格式错误，请输入1000的整数倍";
				}
			}else if(param != null && "decimal".equals(rule)){//带小数的数字校验,最多3位小数
				String regex = "^[0-9]+(.[0-9]{0,3})?$";
				if(param.indexOf(".") != -1 && param.indexOf("E") != -1){
					regex = "^[0-9]+(.[0-9]+[E]+[0-9]{0,10})?$";//科学计数法
				}
				if(!match(regex, param)){
					return keyName+"格式错误，请输入数字（最多3位小数）";
				}
//				if(Double.valueOf(param) == 0){
//					return keyName+"必须大于0";
//				}
	        }else if(param != null && "special".equals(rule)){ //特殊字符校验
				if(ValidDataUtil.hasSpecial(param)){
					return keyName+"包含特殊字符";
				}
	        }else if(param != null && "email".equals(rule)){//电子邮件校验
	        	String regex = "^([\\w-\\.]+)@((\\[[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.)|(([\\w-]+\\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\\]?)$";
				if(!match(regex, param)){
					return keyName+"格式错误";
				}
	        }else if(param != null && "mobile".equals(rule)){//手机号码校验
	        	String regex = "^(((13[0-9]{1})|(15[0-9]{1})|(18[0-9]{1})|(14[0-9]{1})|(17[0-9]{1}))+\\d{8})$";
				if(!match(regex, param)){
					return keyName+"格式错误";
				}
	        }else if(param != null && "idCard".equals(rule)){//身份证号校验
				if(!ValidDataUtil.checkIDCard(param)){
					return keyName+"格式错误";
				}
	        }else if(param != null && "date".equals(rule)){//日期校验
				if(!ValidDataUtil.isDate(param)){
					return keyName+"格式错误";
				}
			}else if(param != null && "url".equals(rule)){
				String regex = "^([hH][tT]{2}[pP]://|[hH][tT]{2}[pP][sS]://)(([A-Za-z0-9-~]+).)+([A-Za-z0-9-~\\\\/])+$";
				if(!match(regex, param)){
					return keyName+"格式错误";
				}
			}
	        return null;
		} catch (Exception e) {
			return "非法参数";
		}
    }

	/**
	 * 校验对象
	 * @param object        待校验对象
	 * @param groups        待校验的组
	 */
	public static void validateEntity(Object object, Class<?>... groups) {
		Set<ConstraintViolation<Object>> constraintViolations;
		if(object instanceof List){
			List list = (List)object;
			for (int i = 0; i < list.size(); i++) {
				validateEntity(list.get(i),groups);
			}
		}else {
			constraintViolations = validator.validate(object, groups);
			if (!constraintViolations.isEmpty()) {
				ConstraintViolation<Object> constraint = (ConstraintViolation<Object>)constraintViolations.iterator().next();
				throw new HmException(constraint.getMessage());
			}
		}

	}
	
	/*public static void main(String[] args){

	}*/
}
